/*-
 *  Copyright (C) 2009 Peter Baldwin   
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.peterbaldwin.client.android.wildlife;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;

/**
 * This {@link ContentProvider} exports assets so that they can be used by
 * external applications (e.g., an image viewer).
 * 
 * Note: It doesn't seem to be possible to share resources directly (i.e., using
 * {@code android.resource://}); they might be private by default.
 */
public class AssetProvider extends ContentProvider {
	public static final Uri CONTENT_URI = Uri
			.parse("content://org.peterbaldwin.wildlife.assets/asset");

	public static final String PARAM_FILE_NAME = "fileName";
	public static final String PARAM_CONTENT_TYPE = "contentType";

	@Override
	public boolean onCreate() {
		return true;
	}

	@Override
	public String getType(Uri uri) {
		// Depend on the caller to know the content type
		// and encode it in the URI.
		return uri.getQueryParameter(PARAM_CONTENT_TYPE);
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		return null;
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		return 0;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		return null;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		return 0;
	}

	private static void saveInputStreamToFile(InputStream in, File file)
			throws IOException {
		File parent = file.getParentFile();
		if (!parent.exists() && !parent.mkdirs()) {
			String msg = "Could not create parent directory: " + parent;
			throw new IOException(msg);
		}
		if (!file.createNewFile()) {
			String msg = "Could not create file: " + file;
			throw new IOException(msg);
		}
		OutputStream out = new FileOutputStream(file);
		out = new BufferedOutputStream(out);
		try {
			while (true) {
				int b = in.read();
				if (b == -1) {
					break;
				}
				out.write(b);
			}
		} finally {
			out.flush();
			out.close();
		}
	}

	@Override
	public ParcelFileDescriptor openFile(Uri uri, String mode)
			throws FileNotFoundException {
		if (!"r".equals(mode)) {
			throw new FileNotFoundException("Bad mode for " + uri + ": " + mode);
		}

		Context context = getContext();
		AssetManager assets = context.getAssets();
		String fileName = uri.getQueryParameter(PARAM_FILE_NAME);
		File file = context.getCacheDir();
		file = new File(file, "assets");
		file = new File(file, fileName);
		boolean success = false;
		try {
			if (!file.exists()) {
				InputStream in = assets.open(fileName);
				in = new BufferedInputStream(in);
				try {
					// ParcelFileDescriptor cannot refer to a subset of a file,
					// so the asset must be saved into its own file.
					saveInputStreamToFile(in, file);
				} finally {
					in.close();
				}
			}
			success = true;
			int modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
			return ParcelFileDescriptor.open(file, modeBits);
		} catch (FileNotFoundException e) {
			throw e;
		} catch (IOException e) {
			String message = "Caused by " + e;
			throw new FileNotFoundException(message);
		} finally {
			if (!success) {
				file.delete();
			}
		}
	}
}
